package ca.uvic.group2.api;

import ca.uvic.group2.api.exceptions.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.*;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Hashtable;
import java.util.StringTokenizer;
import org.apache.log4j.*;

public class QuoteCache {

    public interface QuoteFetcher {
	Quote fetchQuote(int transId, long sessionId, String stockSymbol, String username) throws IOException;
    }

    private static Category debugLog;
    private Hashtable quoteCache;
    private Hashtable waitlist;

    public QuoteCache(Category debugLog) {
	this.debugLog = debugLog;
        quoteCache = new Hashtable();
        waitlist = new Hashtable();
    }

    public void initSystem() throws RemoteException {
        synchronized (quoteCache) {
            quoteCache.clear();
        }
    }

    private Quote getCached(String stockSymbol) {
        Quote q = null;
        synchronized(quoteCache) {
            if(quoteCache.containsKey(stockSymbol)) {
                q = (Quote) quoteCache.get(stockSymbol);
                if(q.expiry < System.currentTimeMillis()) {
                    quoteCache.remove(stockSymbol);
                    q = null;
                }
            }
        }
        if (q != null) {
            debugLog.debug("Cache HIT: " + stockSymbol);
        } else {
            debugLog.debug("Cache MISS: " + stockSymbol);
        }
        return q;
    }

    private Quote getRealQuote(String stock,  String username, int transId, long sessionId, QuoteFetcher quoteFetcher) {
        debugLog.debug("Entering Protected Sections: getRealQuote()");

        Quote q = null;

        QuoteRequest request = null;
        boolean needToWait = false;

        debugLog.debug("Locking on Waitlist: " + stock);
        synchronized(waitlist) {
            if(waitlist.containsKey(stock)) {
                debugLog.debug("Waitlist Contains Stock " + stock);
                request = (QuoteRequest) waitlist.get(stock);
                needToWait = true;
            } else {
                debugLog.debug("Doing another cache lookup");
                q = getCached(stock);
                if(q == null) {
                    debugLog.debug("Cache MISS - Add quote request for " + stock);
                    request = new QuoteRequest(stock);
                    waitlist.put(stock, request);
                }
            }
        }

        debugLog.debug("Determineing Quote");
        if(q == null) {
            if(needToWait) {
                synchronized(request) {
                    q = request.getQuote();
                    if(q == null) {
                        try {
                            debugLog.debug("we are waiting: " + stock);
                            request.wait();
                            debugLog.debug("we got it: " + stock);
                            q = request.getQuote();
                        } catch(InterruptedException e) {
                            debugLog.error("Wait was not Excepted");
                        }
                    }
                }
            } else {
                try {
                    debugLog.debug("We are getting: " + stock);
                    q = quoteFetcher.fetchQuote(transId, sessionId, stock, username);
		    //logger.quote(serverId, transId, q.price, q.stock, q.username, q.timestamp, q.key);
                    cache(stock, q);
                } catch(IOException e) {
                    debugLog.error("Could not contact, Legacy Quote Server.", e);
                    //e.printStackTrace();
                }
                synchronized (waitlist) {
                    debugLog.debug("Remove from waiting: " + stock);
                    waitlist.remove(stock);
                }
                synchronized(request) {
                    debugLog.debug("Set quote and notify: " + stock);
                    request.setQuote(q);
                    request.notifyAll();
                }
            }
        }
        return q;
    }

    private void cache(String sSym, Quote q) {
        debugLog.debug("Caching " + sSym);
        synchronized(quoteCache) {
            quoteCache.put(sSym, q);
        }
        return;
    }

    public Quote quote(int transId, long sessionId, String stockSymbol, String username, 
		       QuoteFetcher quoteFetcher) throws CommException, RemoteException {
        debugLog.debug("quote() request");
        Quote q = null;

        q = getCached(stockSymbol);

        if(q == null) {
            q = getRealQuote(stockSymbol, username, transId, sessionId, quoteFetcher);
        }

        return q;
    }


}

